/**
 * 所有版权归 广西梧州 陈锦韬 所有
 *
 * @Title: MutilTableParameter
 * @Package com.rx.core.dbase.mutil
 * @Description: 多表查询参数
 * @author: 陈锦韬
 * @date: 2021\7\22 0022
 * @version V1.0
 * @Copyright: 2021 陈锦韬  All rights reserved.
 */
package com.rx.core.dbase.mutil.param;

import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.google.common.base.Joiner;
import com.mysql.cj.x.protobuf.MysqlxDatatypes;
import com.rx.core.anno.RxRelationField;
import com.rx.core.anno.RxRelationInfo;
import com.rx.core.base.RxBaseConstant;
import com.rx.core.base.RxBaseData;
import com.rx.core.base.RxScanner;
import com.rx.core.bean.RxConfigBean;
import com.rx.core.data.UrlAnalyse;
import com.rx.core.dbase.RxBaseDbMust;
import com.rx.core.enm.RX_DB_OPERATE;
import com.rx.core.enm.RX_LOG_MODE;
import com.rx.core.enm.RX_VALID;
import com.rx.core.iface.RelationQuery;
import com.rx.core.util.EmptyChecker;
import com.rx.core.util.RxUtil;
import com.rx.core.util.SpringUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author: Administrator
 * @Description: 多表查询参数
 * @date: 2021\7\22 0022
 */
@Data
@Slf4j
public class MutilTableParameter {

    private static List<String> filterFieldList = new ArrayList<>();
    public static final String ALIAS_MAIN = "main.";
    public static final String ALIAS_SECOND = "secondary.";
    public static final String ALIAS_THREE = "three.";
    public static final String NO_REALTION_LIMIT = "<<@#noLimit#@>>";

    protected RxBaseData rxBaseData;
    protected Class mainClass;
    protected Class secondClass;
    protected Class threeClass;

    protected List<Field> mainFieldList;
    protected List<Field> secondFieldList;
    protected List<Field> threeFieldList;

    protected Map<String,Field> mainFieldMap = new HashMap<>();
    protected Map<String,Field> secondFieldMap = new HashMap<>();
    protected Map<String,Field> threeFieldMap = new HashMap<>();

    protected StringBuilder selectSql;
    protected StringBuilder whereSql;
    protected StringBuilder onSql;
    protected StringBuilder threeOnSql;

    protected String mainTable;
    protected String secondTable;
    protected String threeTable;

    protected boolean needFilter = true;

    protected Map<String,StringBuilder> otherConditon = new HashMap<>();

    private boolean filter(RxBaseData rxBaseData,String name){
        if (!needFilter){
            return false;
        }
        List<String> tmpAddList=(List<String>)rxBaseData.getTmpVar(RxBaseData.VAR_SQL_FILTER);
        List<String> tmpRemoveList=(List<String>)rxBaseData.getTmpVar(RxBaseData.VAR_SQL_REMOVE_FILTER);

        if (EmptyChecker.isEmpty(filterFieldList)){
            RxConfigBean rxConfigBean = (RxConfigBean) SpringUtil.getBean("rxConfigBean");
            if(EmptyChecker.isEmpty(rxConfigBean) && EmptyChecker.isEmpty(tmpAddList)){
                return false;
            }

            filterFieldList = Arrays.asList(rxConfigBean.getSqlFilterFields().split(","));
            if (EmptyChecker.isEmpty(filterFieldList) && EmptyChecker.isEmpty(tmpAddList)){
                return false;
            }
        }

        if (EmptyChecker.isEmpty(tmpAddList)){
            tmpAddList = new ArrayList<>(filterFieldList);
        }else if(EmptyChecker.notEmpty(tmpAddList)){
            tmpAddList.addAll(filterFieldList);
        }
        /**
         * 去除过滤字段
         */
        if (EmptyChecker.notEmpty(tmpRemoveList)){
            tmpAddList.removeAll(tmpRemoveList);
        }
        /**
         * 字段过滤
         */
        for (String s : tmpAddList) {
            if(s.trim().equalsIgnoreCase(name)){
                return true;
            }
        }
        return false;
    }

    protected StringBuilder getSql(RxBaseData rxBaseData,String tableAlias,List<Field> fieldList,Map<String,Field> fieldMap){
        StringBuilder stringBuilder = new StringBuilder();
        for (Field field : fieldList) {
            TableField tableField = field.getAnnotation(TableField.class);
            if (null == tableField){
                TableId tableIdField = field.getAnnotation(TableId.class);
                if(RxBaseDbMust.BID.equalsIgnoreCase(tableIdField.value())){
                    if (!filter(rxBaseData,RxBaseDbMust.BID)){
                        stringBuilder.append(tableAlias).append(RxBaseDbMust.BID.toUpperCase()).append(",");
                        fieldMap.put(RxBaseDbMust.BID.toUpperCase(),field);
                    }
                }
            }else {
                if (tableField.exist()&&!filter(rxBaseData,tableField.value())){
                    stringBuilder.append(tableAlias).append(tableField.value()).append(",");
                    fieldMap.put(tableField.value(),field);
                }
            }
        }
        return stringBuilder;
    }

    protected StringBuilder assignSql(Object object,String columnName,String alias){
        if (EmptyChecker.isEmpty(object)){
            return new StringBuilder();
        }
        StringBuilder onSql = new StringBuilder();
        if(object instanceof String){
            onSql.append(" and ").append(alias).append(columnName).append("='").append(object).append("'");
        }else if(object instanceof Long){
            onSql.append(" and ").append(alias).append(columnName).append("='").append(object).append("'");
        }else if(object instanceof Date){
            SimpleDateFormat formatter = new SimpleDateFormat(RxBaseConstant.DATE_FORMAT_DATETIME);
            String dateString = formatter.format(object);
            onSql.append(" and ").append(alias).append(columnName).append("='").append(dateString).append("'");
        }else if(object instanceof BigDecimal){
            onSql.append(" and ").append(alias).append(columnName).append("='")
                    .append(((BigDecimal)object).stripTrailingZeros().toPlainString()).append("'");
        }
        return onSql;
    }

    protected StringBuilder getWhereSql(String columnName,Field field,RxBaseData model,String alias){
        field.setAccessible(true);
        try {
            Object object = field.get(model);
            return assignSql(object,columnName,alias);
        } catch (IllegalAccessException e) {
            log.error(RX_LOG_MODE.ERROR.getFormat("字段值错误"));
        }
        return new StringBuilder();
    }

    /**
     * 判断如何加入条件
     * @param flag
     * @param model
     * @return
     */
    boolean judgeAppendContion(String flag,RxBaseData model){
        if (EmptyChecker.isEmpty(flag)){
            return true;
        }
        String[] flgString = flag.split("=");
        if (flgString.length != 2){
            return true;
        }
        try {
            Field field = RxUtil.Reflection.getField(model.getClass(),flgString[0]);
            Object object = RxUtil.Reflection.getFieldValue(model,field);
            if (EmptyChecker.isEmpty(object)){
                return true;
            }
            if (object instanceof String){
                if(NO_REALTION_LIMIT.equalsIgnoreCase(flgString[1])){
                    return false;
                }
                return ((String) object).equalsIgnoreCase(flgString[1]);
            }else if (object instanceof Long){
                return object.equals(Long.valueOf(flgString[1]));
            }else if (object instanceof Integer){
                return object.equals(Integer.valueOf(flgString[1]));
            }
        } catch (NoSuchFieldException e) {
            return true;
        }
        return true;
    }

    protected void dealRelationInfo(Field field,StringBuilder whereSqlTmp,StringBuilder relationSqlTmp){

    }
    protected StringBuilder getOnSql(String assTable,Map<String,Field> fieldMap,RxBaseData model,String alias,String superAlias ){
        StringBuilder sql = new StringBuilder("1=1");
        defaultValid(model);
        for (Map.Entry<String,Field> entry: fieldMap.entrySet()) {
            Field field = entry.getValue();
            StringBuilder whereSqlTmp = getWhereSql(entry.getKey(),field,model,alias);
            sql.append(whereSqlTmp);
            RxRelationField rxRelationField = field.getAnnotation(RxRelationField.class);
            if (EmptyChecker.isEmpty(rxRelationField)){
                continue;
            }

            RxRelationInfo[] rxRelationInfos = rxRelationField.value();
            for (RxRelationInfo rxRelationInfo : rxRelationInfos) {
                if(!assTable.equalsIgnoreCase(rxRelationInfo.tableName())){
                   continue;
                }
                StringBuilder relationSql = new StringBuilder();
                if (judgeAppendContion(rxRelationInfo.JudgeAppend(),model)){
                    relationSql.append(" and ").append(alias).append(entry.getKey()).append('=')
                            .append(superAlias).append(rxRelationInfo.fieldName());
                    sql.append(relationSql);
                }
                if (RxBaseDbMust.BID.equalsIgnoreCase(rxRelationInfo.fieldName())){
                    dealRelationInfo(field,whereSqlTmp,relationSql);
                }

            }
        }
        return sql;
    }

    private void defaultValid(RxBaseData model){
        if (model == null){
            return;
        }
        if (model instanceof  RxBaseDbMust){
            RxBaseDbMust rxBaseDbMust = (RxBaseDbMust)model;
            RxUtil.nullDo(rxBaseDbMust.getValid(),()->rxBaseDbMust.setValid(RX_VALID.NORMAL.getCode()));
        }
    }
    protected StringBuilder getMainWhere(Map<String,Field> fieldMap,RxBaseData model,String alias){
        StringBuilder sql = new StringBuilder("1=1");
        defaultValid(model);
        for (Map.Entry<String,Field> entry: fieldMap.entrySet()) {
            Field field = entry.getValue();
            sql.append(getWhereSql(entry.getKey(),field,model,alias));
        }
        return sql;
    }

    private StringBuilder assemblinSql(String column,Object value,String op,String alias){
        StringBuilder oneConditon = new StringBuilder();
        oneConditon.append(" and ").append(alias).append(column).append(op).append("'").append(value).append("'");
        otherConditon.put(column,oneConditon);
        return oneConditon;
    }
    protected StringBuilder assignCondition(RxBaseData rxBaseData,String alias){
        Map<String,Object> conditionMap = rxBaseData.getCondition();
        if (EmptyChecker.isEmpty(conditionMap)){
           return new StringBuilder();
        }
        StringBuilder condition = new StringBuilder();
        for (Map.Entry<String, Object> entry : conditionMap.entrySet()) {
            // 解析key。对对应的列做对应操作
            int noMatchAnyOperate = 0;
            StringBuilder oneConditon = new StringBuilder();
            noMatchAnyOperate += RX_DB_OPERATE.IN.assignQueryWrapper(entry.getKey(),(e)->{
                oneConditon.append(" and ").append(alias).append(e).append(" in").append(" ('").append(Joiner.on("','").join(JSONArray.parseArray(JSONArray.toJSONString(entry.getValue()),String.class))).append("')");
                otherConditon.put((String)e,oneConditon);
            });
            noMatchAnyOperate += RX_DB_OPERATE.GT.assignQueryWrapper(entry.getKey(),(e)->oneConditon.append(assemblinSql((String)e,entry.getValue(),">",alias)));
            noMatchAnyOperate += RX_DB_OPERATE.LT.assignQueryWrapper(entry.getKey(),(e)->oneConditon.append(assemblinSql((String)e,entry.getValue(),"<",alias)));
            noMatchAnyOperate += RX_DB_OPERATE.GE.assignQueryWrapper(entry.getKey(),(e)->oneConditon.append(assemblinSql((String)e,entry.getValue(),">=",alias)));
            noMatchAnyOperate += RX_DB_OPERATE.LE.assignQueryWrapper(entry.getKey(),(e)->oneConditon.append(assemblinSql((String)e,entry.getValue(),"<=",alias)));
            noMatchAnyOperate += RX_DB_OPERATE.EQ.assignQueryWrapper(entry.getKey(),(e)->oneConditon.append(assemblinSql((String)e,entry.getValue(),"=",alias)));
            noMatchAnyOperate += RX_DB_OPERATE.NE.assignQueryWrapper(entry.getKey(),(e)->oneConditon.append(assemblinSql((String)e,entry.getValue(),"!=",alias)));

            noMatchAnyOperate += RX_DB_OPERATE.IS_NOT_NULL.assignQueryWrapper(entry.getKey(),(e)->{
                oneConditon.append(" and ").append(alias).append(e).append(" is not null");
                otherConditon.put((String)e,oneConditon);
            });
            noMatchAnyOperate += RX_DB_OPERATE.IS_NULL.assignQueryWrapper(entry.getKey(),(e)->{
                oneConditon.append(" and ").append(alias).append(e).append(" is null");
                otherConditon.put((String)e,oneConditon);
            });
            noMatchAnyOperate += RX_DB_OPERATE.LIKE_LEFT.assignQueryWrapper(entry.getKey(),(e)->{
                oneConditon.append(" and ").append(alias).append(e).append(" like '%").append(entry.getValue()).append("'");
                otherConditon.put((String)e,oneConditon);
            });
            noMatchAnyOperate += RX_DB_OPERATE.LIKE_RIGHT.assignQueryWrapper(entry.getKey(),(e)->{
                oneConditon.append(" and ").append(alias).append(e).append(" like '").append(entry.getValue()).append("%'");
                otherConditon.put((String)e,oneConditon);
            });
            noMatchAnyOperate += RX_DB_OPERATE.LIKE.assignQueryWrapper(entry.getKey(), (e)->{
                oneConditon.append(" and ").append(alias).append(e).append(" like '%").append(entry.getValue()).append("%'");
                otherConditon.put((String)e,oneConditon);
            });
            noMatchAnyOperate += RX_DB_OPERATE.NOT_IN.assignQueryWrapper(entry.getKey(),(e)->{
                oneConditon.append(" and ").append(alias).append(e).append(" not in").append(" ('").append(Joiner.on("','").join(JSONArray.parseArray(JSONArray.toJSONString(entry.getValue()),String.class))).append("')");
                otherConditon.put((String)e,oneConditon);
            });

            if (noMatchAnyOperate == 0){
                // 没有匹配任何一个
                oneConditon.append(" and ").append(alias).append(entry.getKey()).append("=").append("'").append(entry.getValue()).append("'");
                otherConditon.put(entry.getKey(),oneConditon);
            }
            condition.append(oneConditon);

        }
        return condition;
    }
    MutilTableParameter() {

    }
    MutilTableParameter(RxBaseData rxBaseData) {
        this.rxBaseData = rxBaseData;
        mainClass = rxBaseData.getClass();
        UrlAnalyse urlAnalyse = rxBaseData.getUrlAnalyse();
        mainTable = RxScanner.getTableName(urlAnalyse.getForm());
        String table[] = urlAnalyse.getService().split("_");
        secondTable = RxScanner.getTableName(table[0]);

        mainFieldList = RxScanner.getFieldByName(urlAnalyse.getForm());
        secondFieldList = RxScanner.getFieldByName(table[0]);

        if (table.length > 1){
            // 三表查询
            secondClass = RxScanner.getClassByName("double-" + table[0]);
            threeTable = RxScanner.getTableName(table[1]);
            threeClass = RxScanner.getClassByName(table[1]);
            threeFieldList = RxScanner.getFieldByName(table[1]);
        }else{
            secondClass = RxScanner.getClassByName(table[0]);
        }

        // 获取查询字段。
        selectSql = getSql(rxBaseData,ALIAS_MAIN,mainFieldList,mainFieldMap);
        StringBuilder secondSql = getSql(rxBaseData,ALIAS_SECOND,secondFieldList,secondFieldMap);
        RxUtil.nullDo(rxBaseData.getTmpVar(RxBaseData.VAR_SQL_SECOND_FILTER),()->selectSql.append(secondSql));

        if (table.length > 1){
            // 三表查询
            StringBuilder threeSql = getSql(rxBaseData,ALIAS_THREE,threeFieldList,threeFieldMap);
            RxUtil.nullDo(rxBaseData.getTmpVar(RxBaseData.VAR_SQL_THREE_FILTER),()->selectSql.append(threeSql));
        }
        selectSql.deleteCharAt(selectSql.length() - 1);

        // 获取查询条件
        whereSql = getMainWhere(mainFieldMap,rxBaseData,ALIAS_MAIN);
        whereSql.append(assignCondition(rxBaseData,ALIAS_MAIN));

        RxBaseData secondModel = (RxBaseData)((RelationQuery) rxBaseData).obtainRelation();
        if (EmptyChecker.isEmpty(secondModel)){
            try {
                secondModel = (RxBaseData)secondClass.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                log.error(RX_LOG_MODE.ERROR.getFormat("双表查询生成条件失败"));
            }
        }

        StringBuilder secondCondition = assignCondition(secondModel,ALIAS_SECOND);
        onSql = getOnSql(mainTable,secondFieldMap,secondModel,ALIAS_SECOND,ALIAS_MAIN);
        onSql.append(secondCondition);
        if (table.length > 1){
            // 三表查询
            RxBaseData threeModel = (RxBaseData)((RelationQuery) secondModel).obtainRelation();
            if (EmptyChecker.isEmpty(threeModel)){
                try {
                    threeModel = (RxBaseData)threeClass.newInstance();
                } catch (InstantiationException | IllegalAccessException e) {
                    log.error(RX_LOG_MODE.ERROR.getFormat("双表查询生成条件失败"));
                }
            }
            threeOnSql = getOnSql(secondTable,threeFieldMap,threeModel,ALIAS_THREE,ALIAS_SECOND);
            threeOnSql.append(assignCondition(threeModel,ALIAS_THREE));
        }
    }
}
